package cn.antho.basic.service.impl;

import cn.antho.basic.config.BussinessException;
import cn.antho.basic.constant.BaseConstant;
import cn.antho.basic.dto.AccountLoginDto;
import cn.antho.basic.dto.SmsLoginDto;
import cn.antho.basic.dto.WechatLoginDto;
import cn.antho.basic.jwt.JwtUtils;
import cn.antho.basic.jwt.RsaUtils;
import cn.antho.basic.jwt.UserInfo;
import cn.antho.basic.service.ILoginService;
import cn.antho.basic.util.AjaxResult;
import cn.antho.basic.util.HttpUtil;
import cn.antho.basic.util.MD5Utils;
import cn.antho.basic.util.StrUtils;
import cn.antho.system.domain.Menu;
import cn.antho.system.mapper.MenuMapper;
import cn.antho.system.mapper.PermissionMapper;
import cn.antho.user.domain.Logininfo;
import cn.antho.user.domain.User;
import cn.antho.user.domain.Wxuser;
import cn.antho.user.mapper.LogininfoMapper;
import cn.antho.user.mapper.UserMapper;
import cn.antho.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Value("${jwt.rsa.pri}")
    private String privateRsaKey;

    /**
     * 账号密码登录
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> accountLogin(AccountLoginDto dto) {
        // 校验非空
        // 校验用户名是否存在
        Logininfo logininfo = logininfoMapper.findByUsername(dto.getUsername(), dto.getType());
        if (null == logininfo) {
            throw new BussinessException("用户名或密码错误！");
        }
        // 校验用户名密码是否正确
        String encrypPassword = MD5Utils.encrypByMd5(logininfo.getSalt() + dto.getPassword());
        if (StringUtils.isBlank(encrypPassword) || !encrypPassword.equals(logininfo.getPassword())) {
            throw new BussinessException("用户名或密码错误！");
        }
        return getMap(logininfo);

    }

    private Map<String, Object> getMap(Logininfo logininfo) {
        // 使用JWT
        PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(privateRsaKey).getFile());
        HashMap<String, Object> hashMap = new HashMap<>();
        UserInfo user = new UserInfo();
        user.setLogininfo(logininfo);
        // 判断logininfo是否是管理员
        if (0 == logininfo.getType()) {
            // 如果是管理员，将权限放到token中
            List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
            List<Menu> menus = menuMapper.loadOwnMenuByLogininId(logininfo.getId());
            user.setPermissions(permissions);
            user.setMenus(menus);
            hashMap.put("permissions", permissions);
            hashMap.put("menus", menus);
        }
        // 获取token
        String token = JwtUtils.generateTokenExpireInMinutes(user, privateKey, 300);
        // 将logininfo的敏感字段置空
        logininfo.setPhone("");
        logininfo.setPassword("");
        logininfo.setSalt("");
        // 将map返回给前端
        hashMap.put("token", token);
        hashMap.put("logininfo", logininfo);
        return hashMap;
    }

//    private Map<String, Object> getMap(Logininfo logininfo) {
//        // 将token和logininfo放到redis中
//        String token = UUID.randomUUID().toString();
//        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
//        // 将敏感字段置空，保证安全性
//        logininfo.setPassword("");
//        logininfo.setPhone("");
//        logininfo.setSalt("");
//        // 返回给前端
//        Map<String, Object> map = new HashMap<String, Object>();
//        map.put("token", token);
//        map.put("logininfo", logininfo);
//        return map;
//    }

    /**
     * 短信登录
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> smsLogin(SmsLoginDto dto) {
        // 校验非空
        // 校验手机号是否存在
        Logininfo logininfo = logininfoMapper.findByPhone(dto.getPhone());
        if (null == logininfo) {
            throw new BussinessException("手机号不存在！");
        }
        // 判断验证码是否正确
        String redisSmsCode = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerfityCodeCons.LOGIN_SMSCODE_PHONE, dto.getPhone()));
        String[] split = redisSmsCode.split(":");
        if (StringUtils.isBlank(redisSmsCode) || !split[0].equals(dto.getSmsCode())) {
            throw new BussinessException("验证码错误！");
        }
        return getMap(logininfo);
    }

    /**
     * 微信扫码登录
     * @param dto
     */
    @Override
    public AjaxResult wechatLogin(WechatLoginDto dto) {
        // 判断code是否为空
        if (StringUtils.isBlank(dto.getCode())) {
            throw new BussinessException("未获取到code");
        }
        // 获取token和openId
        Pair<String, String> tokenAndOpenid = this.getTokenAndOpenid(dto.getCode());
        String token = tokenAndOpenid.getLeft();
        String openid = tokenAndOpenid.getRight();
        System.out.println(token);
        System.out.println(openid);
        // 根据openId判断wx_user中是否有数据
        Wxuser wxuser = wxuserMapper.findByOpenid(openid);
        if (null != wxuser && null != wxuser.getId()) {
            // 已经有数据，表示已经登陆过，就直接登录
            Logininfo logininfo = logininfoMapper.findByUserId(wxuser.getUserId());
            Map<String, Object> map = getMap(logininfo);
            return AjaxResult.me().success(map);
        }
        // 没有数据，跳转到绑定页面
        StringBuffer sb = new StringBuffer()
                .append("?accessToken=")
                .append(token)
                .append("&openId=")
                .append(openid);
        return AjaxResult.me().fail("nobinder").setResultObj(sb);
    }

    /**
     * 微信登录绑定
     * @param dto
     */
    @Override
    public Map<String,Object> wechatBinder (WechatLoginDto dto) {
        // 校验参数非空
        // 校验验证码是否正确
        this.verifySmsCode(dto);
        System.out.println(dto.toString());
        // 调用第三个微信接口 获取用户信息
        String url = String.format(BaseConstant.WxLoginCons.WX_LOGIN_USER_INFO, dto.getToken(), dto.getOpenid());
        System.out.println(url);
        String result = HttpUtil.httpGet(url);
        System.out.println(result);
        // 转换
        Wxuser wxuser = JSONObject.parseObject(result, Wxuser.class);
        System.out.println(wxuser.toString());
        // 根据手机号查询user中是否有数据，有就存一张wxuser表，没有就存user、logininfo、wxuser三张表
        User user = userMapper.findByPhone(dto.getPhone());
        Logininfo logininfo = new Logininfo();
        if (null == user) {
            // 初始化user对象
            user= initUser(dto);
            // 初始化logininfo对象
            logininfo = initLogininfo(user);
            // 保存logininfo
            logininfoMapper.add(logininfo);
            // 保存user
            user.setLogininfoId(logininfo.getId());
            userMapper.add(user);
        } else {
            // 获取logininfo对象
            logininfo = logininfoMapper.findByUserId(user.getId());
        }
        // 保存到wxuser表
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
        return getMap(logininfo);

    }

    // 初始化User
    private User initUser (WechatLoginDto dto) {
        // 生成盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 密码 默认123456
        String encrypPassword = MD5Utils.encrypByMd5(salt + "123456");
        return User.builder()
                .username(dto.getPhone())
                .phone(dto.getPhone())
                .salt(salt)
                .password(encrypPassword)
                .createtime(new Date())
                .state(1)
                .build();
    }

    // 初始化logininfo
    private Logininfo initLogininfo (User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        logininfo.setType(1);
        return logininfo;
    }

    // 验证验证码是否正确
    private void verifySmsCode(WechatLoginDto dto) {
        String redisSmsCode = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerfityCodeCons.BINDER_SMSCODE_PHONE, dto.getPhone()));
        if (StringUtils.isBlank(redisSmsCode)) {
            throw new BussinessException("验证码错误！");
        }
        String smsCode = redisSmsCode.split(":")[0];
        if (!smsCode.equals(dto.getVerifyCode())) {
            throw new BussinessException("验证码错误！");
        }
    }

    // 通过code获取token和openid
    private Pair<String, String> getTokenAndOpenid (String code) {
        // 获取token的请求地址
        String tokenUrl = String.format(
                BaseConstant.WxLoginCons.WX_LOGIN_TOKEN_URL,
                BaseConstant.WxLoginCons.WX_LOGIN_APPID,
                BaseConstant.WxLoginCons.WX_LOGIN_SECRET,
                code);
        // 发送获取token请求
        String result = HttpUtil.httpGet(tokenUrl);
        // 将字符串转换为JSON对象
        JSONObject jsonObject = JSONObject.parseObject(result);
        // 从JSON对象中获取token和openid
        String token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        // 返回Map对象 集合 pair
        return Pair.of(token, openid);
    }

}
